﻿using System;
using System.Runtime.InteropServices;
using System.IO;

namespace CEED.Utils
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct String8
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public char[] text;
    }

    public class NCCHPlaingRegion
    {
        public string[] PlainRegionStrings;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHHeader
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
        public byte[] NCCHHeaderSignature;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public char[] Magic;
        public uint CXILength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] TitleID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public char[] MakerCode;
        public ushort Version;

        public uint SEEDCheck;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] ProgramID;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] Unknown0_0;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public byte[] LogoRegionHash;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public char[] ProductCode;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public byte[] ExtendedHeaderHash;
        public uint ExtendedHeaderSize;

        public uint Unknown1;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] Flags;

        public uint PlainRegionOffset;
        public uint PlainRegionSize;

        public uint LogoRegionOffset;
        public uint LogoRegionLength;

        public uint ExeFSOffset;
        public uint ExeFSLength;
        public uint ExeFSHashRegionSize;

        public uint Unknown5;

        public uint RomFSOffset;
        public uint RomFSLength;
        public uint RomFSHashRegionSize;

        public uint Unknown6;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
        public byte[] ExeFSSuperBlockhash;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
        public byte[] RomFSSuperBlockhash;

        public static NCCHPlaingRegion getPlainRegionStringsFrom(byte[] buffer)
        {
            NCCHPlaingRegion temp = new NCCHPlaingRegion();
            string bigstring = System.Text.ASCIIEncoding.ASCII.GetString(buffer);
            string[] splited = bigstring.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries);
            temp.PlainRegionStrings = splited;
            return temp;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeader
    {
        public NCCHExtendedHeaderCodeSetInfo CodeSetInfo;
        public NCCHExtendedHeaderDependencyList DependencyList;
        public NCCHExtendedHeaderSystemInfo SystemInfo;
        public NCCHExtendedHeaderArm11SystemLocalCaps Arm11SystemLocalCaps;
        public NCCHExtendedHeaderArm11KernelCaps Arm11KernelCaps;
        public NCCHExtendedHeaderArm9AccessControl Arm9AccessControl;
    }
    public struct NCCHAccessControlExtend
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x100)]
        public byte[] AccessDescSignature;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x100)]
        public byte[] NCCHHdrPublicKey;
        public NCCHExtendedHeaderArm11SystemLocalCaps Arm11SystemLocalCaps;
        public NCCHExtendedHeaderArm11KernelCaps Arm11KernelCaps;
        public NCCHExtendedHeaderArm9AccessControl Arm9AccessControl;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderCodeSetInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public char[] Name;
        public NCCHExtendedHeaderSystemInfoFlags Flags;
        public NCCHExtendedHeaderCodeSegmentInfo Text;
        public uint StackSize;
        public NCCHExtendedHeaderCodeSegmentInfo Ro;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] Reserved;
        public NCCHExtendedHeaderCodeSegmentInfo Data;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] BssSize;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderSystemInfoFlags
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
        public byte[] Reserved;
        public byte Flag;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] RemasterVersion;

    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderCodeSegmentInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public byte[] Address;
        public uint NumMaxPages;
        public uint CodeSize;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderDependencyList
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)]
        public String8[] ProgramID;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderSystemInfo
    {
        public ulong SaveDataSize;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] JumpID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x30)]
        public byte[] Reserved2;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderStorageInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] ExtSaveDataID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] SystemSaveDataID;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] Reserved;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
        public byte[] AccessInfo;
        public byte OtherAttributes;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ResourceLimit
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] Data;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderArm11SystemLocalCaps
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] ProgramID;
        public uint CoreVersion;
        public ushort Flags;
        public byte Flag0;
        public byte Priority;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
        public ResourceLimit[] ResourceLimitDescriptor;
        public NCCHExtendedHeaderStorageInfo StorageInfo;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
        public String8[] ServiceAccessControl;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1f)]
        public byte[] Reserved;
        public byte ResourceLimitCategory;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderArm11KernelCaps
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x70)]
        public byte[] Descriptors;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
        public byte[] Reserved;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct NCCHExtendedHeaderArm9AccessControl
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xF)]
        public byte[] Descriptors;
        public byte ARM9DescriptorVersion;
    }

    public class NCCH
    {
        public NCCHHeader Header;
        public NCCHExtendedHeader ExHeader;
        public NCCHAccessControlExtend AccessControlExtend;

        public NCCH(Stream stream)
        {
            Header = StructReader.ReadStruct<NCCHHeader>(stream);
        }
    }

}
